﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Collections;

namespace TableEditorNew.utils
{
    public class CSVImport
    {
        public static List<string> GetColumnNames(string filename, string delimiter)
        {
            string[] fields = null;
            string line;

            using (System.IO.TextReader tr = new System.IO.StreamReader(filename))
            {
                while (tr.Peek() != -1)
                {
                    line = tr.ReadLine();

                    if (line == string.Empty)
                    {
                        // Do nothing
                    }
                    else
                    {
                        // Get column-names
                        if (delimiter == "Tab")
                        {
                            fields = line.Split(new char[] { '\t' });
                        }
                        else
                        {
                            fields = line.Split(Convert.ToChar(delimiter));
                        }

                        break;
                    }

                }
            }

            return fields.ToList<string>();
        }

        public static System.Data.DataTable GetData(string filename, string delimiter)
        {
            string line = string.Empty;
            string[] fields = null;
            bool columnNamesLine = true;
            DataRow dr;
            DataTable dt = new DataTable();

            using (System.IO.TextReader tr = new System.IO.StreamReader(filename))
            {
                while (tr.Peek() != -1)
                {
                    line = tr.ReadLine();

                    if (line == string.Empty)
                    {
                        // Do nothing
                    }
                    else if (columnNamesLine)
                    {
                        if (delimiter == "Tab")
                        {
                            fields = line.Split(new char[] { '\t' });
                        }
                        else
                        {
                            fields = line.Split(Convert.ToChar(delimiter));
                        }

                        FormatValues(fields);

                        CreateTable(fields, dt);
                        columnNamesLine = false;
                    }
                    else
                    {
                        if (delimiter == "Tab")
                        {
                            fields = line.Split(new char[] { '\t' });
                        }
                        else
                        {
                            fields = line.Split(Convert.ToChar(delimiter));
                        }

                        FormatValues(fields);

                        // Check if row is valid
                        ConditionValues(fields, dt);

                        // Insert values into table
                        dr = dt.NewRow();
                        for (int i = 0; i < fields.Length; i++)
                        {
                            dr[i] = fields[i];
                        }

                        dt.Rows.Add(dr);
                    }
                }
            }

            return dt;
        }

        /// <summary>
        /// Format the values.
        /// </summary>
        private static void FormatValues(string[] fields)
        {
            for (int i = 0; i < fields.Length; i++)
            {
                fields[i] = fields[i].Trim();
                if ((fields[i].StartsWith(((char)34).ToString()) &&
                fields[i].EndsWith(((char)34).ToString())) ||
                (fields[i].StartsWith("'") && fields[i].EndsWith("'")))
                {
                    fields[i] = fields[i].Substring(1, fields[i].Length - 2);
                }
            }
        }

        /// <summary>
        /// Create the table to contain the csv
        /// </summary>
        private static void CreateTable(string[] fields, DataTable dt)
        {
            DataColumn col;
            DataRow dr;

            foreach (string a in fields)
            {
                col = new DataColumn();
                col.DataType = Type.GetType("System.String");
                col.ColumnName = a;
                dt.Columns.Add(col);
            }

            dr = dt.NewRow();
            for (int i = 0; i < dt.Columns.Count; i++)
            {
                dr[i] = dt.Columns[i].ColumnName;
            }

            dt.Rows.Add(dr);
        }


        /// <summary>
        /// Check to make sure that something didn't get split by a comma when it shouldn't
        /// </summary>
        /// <param name="vals">The values of one line.</param>
        private static void ConditionValues(string[] vals, DataTable dt)
        {
            // Check to make sure that something didn't get split by a comma when it
            // shouldn't have... e.g., "Reston" and " VA" should have been "Reston, VA"
            // First Pass:
            if (vals.Length != dt.Columns.Count)
            {
                ArrayList unmatchedStarting = new ArrayList();
                ArrayList unmatchedEnding = new ArrayList();
                for (int i = 0; i < vals.Length - 1; i++)
                {
                    if (vals[i].StartsWith(((char)34).ToString()) &&
                    !vals[i].EndsWith(((char)34).ToString()))
                    {
                        unmatchedStarting.Add(i);
                    }
                    else if (vals[i].EndsWith(((char)34).ToString())
                    && !vals[i].StartsWith(((char)34).ToString()))
                    {
                        unmatchedEnding.Add(i);
                    }
                }

                if (unmatchedStarting.Count != unmatchedEnding.Count)
                {
                    ConditionValues_OneCommaOnly(vals); // Fallback -has some disadvantages
                }
                else
                {
                    ArrayList newVals = new ArrayList();
                    string append = string.Empty;
                    bool appending = false;
                    for (int i = 0; i < vals.Length; i++)
                    {
                        if (!appending &&
                        !unmatchedStarting.Contains(i) && !unmatchedEnding.Contains(i))
                        {
                            newVals.Add(vals[i]);
                        }
                        else if (Convert.ToInt32(unmatchedEnding[0]) == i)
                        {
                            unmatchedStarting.RemoveAt(0);
                            unmatchedEnding.RemoveAt(0);
                            append += vals[i];
                            newVals.Add(append);
                            append = string.Empty;
                            appending = false;
                        }
                        else
                        {
                            appending = true;
                            append += vals[i] + ",";
                        }
                    }

                    vals = (string[])newVals.ToArray(vals[0].GetType());
                }
            }

            for (int i = 0; i < vals.Length; i++)
            {
                vals[i] = vals[i].Trim();
                if ((vals[i].StartsWith(((char)34).ToString()) &&
                vals[i].EndsWith(((char)34).ToString())) ||
                (vals[i].StartsWith("'") && vals[i].EndsWith("'")))
                {
                    vals[i] = vals[i].Substring(1, vals[i].Length - 2);
                }
            }
        }

        /// <summary>
        /// Check to make sure that something didn't get split by a comma when it shouldn't
        /// </summary>
        /// <param name="vals">The values of one line.</param>
        private static void ConditionValues_OneCommaOnly(string[] vals)
        {
            // Check to make sure that something didn't get split by a comma when it
            // shouldn't have... e.g., "Reston" and " VA" should have been "Reston, VA"
            for (int i = 0; i < vals.Length; i++)
            {
                vals[i] = vals[i].Trim();
            }

            int endCond = vals.Length - 2;
            for (int i = 0; i <= endCond; i++)
            {
                // If endcond changes, .NET seems to still cache the old endCond.
                // Helpful.
                if (i > endCond)
                {
                    break;
                }

                if (vals[i].StartsWith(((char)34).ToString()) &&
                !vals[i].EndsWith(((char)34).ToString()) && !vals[i +
                1].StartsWith(((char)34).ToString()) && vals[i +
                1].EndsWith(((char)34).ToString()))
                {
                    // We probably have a problem here...
                    string[] newVals = new string[vals.Length - 2];

                    for (int q = 0; i <= vals.Length; i++)
                    {
                        // Reload, skipping item i and merging back in
                        if (q < i)
                        {
                            newVals[q] = vals[q];
                        }
                        else if (q == i)
                        {
                            newVals[q] = vals[q] + ", " + vals[q + 1];
                        }
                        else
                        {
                            newVals[q] = vals[q + 1];
                        }
                    }

                    // Update vals
                    vals = newVals;
                    endCond = vals.Length - 2;

                    // Reset i to rescan all
                    i = 0;
                }
            }

            for (int i = 0; i < vals.Length; i++)
            {
                if ((vals[i].StartsWith(((char)34).ToString()) &&
                vals[i].EndsWith(((char)34).ToString())) ||
                (vals[i].StartsWith("'") && vals[i].EndsWith("'")))
                {
                    vals[i] = vals[i].Substring(1, vals[i].Length - 2);
                }
            }
        }

    }
}
